/* console implementation */

/* Copyright (C) 2009-2012 David Zanetti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <stdio.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <string.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#include "global.h"
#include "console.h"
#include "serial.h"
#include "ip.h"
//#include "rtc.h"
#include "ds3234.h"
// #include "venus634.h"
#include "encx24j600.h"
#include "gps.h"
#include "xbootapi.h"
#include "tftp.h"

void print_version(void);

/* our hostname from EEPROM */
uint8_t EEMEM hostname_eeprom[16];

void hostname_init(void) {
	/* copy the hostname out of the eeprom and see the global value for it */
	eeprom_read_block((void *) &hostname, (const void *) &hostname_eeprom, 16);
	hostname_len = strlen(hostname);
}

void console_init(void) {
	/* init the serial port and print out the banner */
	serial0_init();

	serial0_tx_cout('\f');

	serial0_tx_PGM(PSTR("Hardware Stratum-1 NTP Server\r\n"));
	serial0_tx_PGM(PSTR("Copyright 2009-2011 David Zanetti\r\n"));
	serial0_tx_PGM(PSTR("Licensed under GPLv2 :: hairy.geek.nz/ntp\r\n"));
	print_version();
	serial0_tx_cr();

	/* report on what we were reset from */
#if 0
	/* FIXME: port reset condition reporting to XMEGA */
	switch (MCUSR) {
		case (1 << JTRF):
				serial0_tx_PGM(PSTR("reset: coming out of JTAG reset\r\n"));
		serial0_tx_cr();
		break;
		case (1 << WDRF):
				serial0_tx_PGM(PSTR("reset: watchdog reset detected\r\n"));
		serial0_tx_cr();
		break;
		case (1 << BORF):
				serial0_tx_PGM(PSTR("reset: brown-out reset detected\r\n"));
		serial0_tx_cr();
		break;
		case (1 << EXTRF):
				serial0_tx_PGM(PSTR("reset: hard external reset\r\n"));
		serial0_tx_cr();
		break;
	}

	MCUSR = 0; /* clear the flags */
#endif

	serial0_tx_PGM(PSTR("cpu: "));
	serial0_tx_dec(F_CPU / 1000000);
	if (F_CPU % 1000000) {
		serial0_tx_cout('.');
		serial0_tx_dec(F_CPU % 1000000);
	}
	serial0_tx_PGM(PSTR("MHz\r\n"));

}

void print_version(void) {
	serial0_tx_PGM(PSTR("NTP Server Build r"));
	serial0_tx_PGM(PSTR(SVNVER));
	serial0_tx_PGM(PSTR(" ("));
	serial0_tx_PGM(PSTR(BUILDDATE));
	serial0_tx_PGM(PSTR(")\r\n"));
}

/* process console messages */
void console_process(void) {
	char com, s = 0;
	uint8_t n = 0;
#ifdef IP_H
	uint8_t x[4] = {0, 0, 0, 0};
#endif

	/* get the command */
	com = serial0_rx();
	/* force output to new line */
	serial0_tx_PGM(PSTR("\r\n"));

	/* switch on what command we got */
	switch (com) {
		case 'b':
		case 'B':
				/* break out to bootloader */
				xboot_reset();
				/* never reached */
				break;
		case 'u':
		case 'U':
				/* TFTP testing hook */
				{
					uint8_t addr[4] = {10,32,34,1};
					char filename[32];
					snprintf(filename,31,"ntp-1.4-%02x%02x%02x%02x%02x%02x",eth_mac[0],eth_mac[1],eth_mac[2],eth_mac[3],eth_mac[4],eth_mac[5]);
					tftp_request(addr,filename);
				}
				break;
		case 'p':
		case 'P':
				/* passthru mode on the GPS port. This touches the watchdog so we don't get reset */
				gps_passthru();
				break; /* never reached */
		case 'q':
		case 'Q':
				/* send the request for UTC data */
				gps_request_utc();
				break;
		case 'H':
		case 'h':
			s = serial0_rx(); /* consume space */
			if (s != '=') {
				if (s == '?') {
					serial0_tx_PGM(PSTR("Hostname: "));
					serial0_tx(hostname,hostname_len);
					serial0_tx_PGM(PSTR("\r\n"));
					break;
				}
				serial0_tx_PGM(PSTR("ERR\r\n"));
				break;
			}
			while (n < 15 && s != '\r' && (flag_serial & FLAG_SERIAL_S0_RX)) {
				s = serial0_rx();
				hostname[n] = s;
				n++;
			}
			hostname[n-1] = 0;
			hostname_len = strlen(hostname);
			/* copy hostname into EEPROM */
			eeprom_write_block(&hostname, &hostname_eeprom, hostname_len+1);
			serial0_tx_PGM(PSTR("OK\r\n"));
			break;
#ifdef IP_H
		case 'i':
		case 'I':
			s = serial0_rx();
			if (s != '=') {
				if (s == '?') {
					serial0_tx_PGM(PSTR("IP Address: "));
					ip_printaddr(ip_addr);
					serial0_tx_PGM(PSTR("\r\n"));
					break;
				}
				serial0_tx_PGM(PSTR("ERR\r\n"));
				break;
			}
			while (n < 12 && s != '\r' && (flag_serial & FLAG_SERIAL_S0_RX)) {
				/* power of operator appears broken, implement fixed */
				uint8_t z[3] = { 100, 10, 1 };

				s = serial0_rx();
				if (s < 0x30 || s > 0x39) {
					break;
				}
				x[n / 3] += (s - 0x30) * z[n % 3];
				n++;
			}
			/* parse address to make sure it's good */
			if (n != 12 || x[0] > 255 ||
					x[1] > 255 ||
					x[2] > 255 ||
					x[3] > 255) {
				serial0_tx_PGM(PSTR("ERR\r\n"));
				break;
			}
			ip_update(x[0], x[1], x[2], x[3]);
			serial0_tx_PGM(PSTR("OK\r\n"));
			break;
#endif
#ifdef VENUS634_H
		case 'v':
		case 'V':
			s = serial0_rx();
			if (s != '?') {
				serial0_tx_PGM(PSTR("ERR\r\n"));
				break;
			}
			print_version();
			venus634_version();
			break;
#endif
		case 's':
		case 'S':
			s = serial0_rx();
			if (s != '?') {
				serial0_tx_PGM(PSTR("ERR\r\n"));
				break;
			}
			/* stuff */
			serial0_tx_PGM(PSTR("Uptime: "));
			serial0_tx_dec(global_counters.uptime / 86400);
			serial0_tx_PGM(PSTR("d "));
			serial0_tx_dec((global_counters.uptime % 86400) / 3600);
			serial0_tx_PGM(PSTR("h "));
			serial0_tx_dec((global_counters.uptime % 3600) / 60);
			serial0_tx_PGM(PSTR("m "));
			serial0_tx_dec(global_counters.uptime % 60);
			serial0_tx_PGM(PSTR("s "));

			if (flag_gps & FLAG_GPS_LOCK) {
				serial0_tx_PGM(PSTR("\r\nGPS: Locked ["));
				serial0_tx_dec(gps_sat_count);
				serial0_tx_PGM(PSTR(" satellites]; Frames: "));
			} else {
				serial0_tx_PGM(PSTR("\r\nGPS: No Lock; Frames: "));
			}
			serial0_tx_dec(global_counters.gps_frame_good);
			serial0_tx_PGM(PSTR(" good, "));
			serial0_tx_dec(global_counters.gps_frame_bad);
			serial0_tx_PGM(PSTR(" bad, "));
			serial0_tx_dec(global_counters.gps_frame_ignored);
			serial0_tx_PGM(PSTR(" ignored\r\n"));

			if (flag_clock & FLAG_CLOCK_SYNC) {
				serial0_tx_PGM(PSTR("Clock: Synced [cur:"));
			} else {
				serial0_tx_PGM(PSTR("Clock: Not Synced [cur:"));
			}
			ds3234_printtime();
			//serial0_tx_dec(rtc_s);
			serial0_tx_PGM(PSTR("]; Resyncs: "));
			serial0_tx_dec(global_counters.clock_resync);
			if (flag_clock & FLAG_CLOCK_SLEW) {
				serial0_tx_PGM(PSTR(" (slewing)\r\n"));
			} else {
				serial0_tx_PGM(PSTR("\r\n"));
			}

			serial0_tx_PGM(PSTR("Ethernet: "));
			if (flag_ether & FLAG_ETHER_LINK) {
				serial0_tx_PGM(PSTR("Up ["));
				encx24j600_print_linkstate();
				serial0_tx_cout(']');
			} else {
				serial0_tx_PGM(PSTR("Down"));
			}
			serial0_tx_PGM(PSTR("; "));
			serial0_tx_dec(global_counters.eth_pkt_rx);
			serial0_tx_PGM(PSTR(" RX, "));
			serial0_tx_dec(global_counters.eth_pkt_tx);
			serial0_tx_PGM(PSTR(" TX\r\n"));
			serial0_tx_PGM(PSTR("IP: "));
			ip_printaddr(ip_addr);
			serial0_tx_cout('/');
			ip_printaddr(ip_mask);
			serial0_tx_PGM(PSTR(" gw "));
			ip_printaddr(ip_gateway);
			serial0_tx_cr();

			serial0_tx_PGM(PSTR("TCXO: temp "));
			ds3234_print_temp();
			serial0_tx_cr();

			break;
		case 'x':
		case 'X':
			clear_counters();
			serial0_tx_PGM(PSTR("OK\r\n"));
			break;
		default:
			serial0_tx_PGM(PSTR("ERR\r\n"));
			break;
	}
	serial0_tx(hostname, hostname_len);
	serial0_tx_PGM(PSTR("# "));

	/* clean up any waiting input */
	while ((flag_serial & FLAG_SERIAL_S0_RX) && s != '\r') {
		s = serial0_rx();
	}
}

void clear_counters(void) {
	global_counters.gps_frame_good = 0;
	global_counters.gps_frame_bad = 0;
	global_counters.gps_frame_ignored = 0;
	global_counters.eth_pkt_tx = 0;
	global_counters.eth_pkt_rx = 0;
	global_counters.clock_resync = 0;
}

void console_eeprom_init(void) {
	char hostname[16] = HOSTNAME;
	eeprom_write_block(&hostname,&hostname_eeprom,HOSTNAME_LEN);
}
